雖然鐵人賽已暫告一個段落,但在[Day 27] Edge Impulse + BLE Sense實現影像分類(上)有提到採購的OV760(無FIFO)攝影機模組尚未到手,所以只能暫以預先準備好的影像集上傳來做實驗。上週拿到後就急忙進行實驗,結果一波N折,處處碰壁,直到昨晚(2021/10/20)才算初步搞定,所以今天補上這篇方便大家可以測試一下。
原先Edge Impulse官網推薦Arduino Nano 33 BLE Sense(以下簡稱BLE Sense)搭配OV7675來做視覺相關應用。而OV7670(無FIFO)攝影機模組取像規格相近,也滿常用於Arduino其它產品上,價格相當便宜,且使用M12鏡頭可手動調整焦距,更方便應用於各種視覺應用,所以決定改用OV7670來連接BLE Sense再上傳到Edge Impulse。
如果大家用Google搜尋BLE Sense如何連接到OV7670,大概通常都會找到這篇Machine vision with low-cost camera modules,本來想跟著做一遍就收工,無奈老天給了我更多的考驗,以下就讓我緩緩道來到底遇到什麼問題吧。
首先當然要把OV7670和BLE Sense連接起來,通常網路上的範例都是用麵包板和杜邦線連結,為了後續實驗更方便,這裡採用直接焊洞洞板的方式完成,並令攝影機模組板和BLE Sense板呈90度連接,方便更替,大家可自行決定用那種方式連接。完整接線圖如圖31-1所示,或參考Table 33-1。在[Day 27]有提及OV7675的PEN/RST(Pin 19), PWDN/PDN(Pin20)是對應到OV7670的Pin 17, 18,但經查閱相關資料後得知,不接亦可。
Tabel 33-1 OV7670和Arduino Nano 33 BLE Sense接線對照表
OV7670 Pin Name | OV7670 Pin Number | BLE Sense Pin Name | 備註 |
---|---|---|---|
3.3V | 01 | 3.3V | |
GND | 02 | GND | 任一個GND皆可 |
SCL | 03 | SCL/A5 (P0.02) | |
SDA | 04 | SDA/A4 (P0.31) | |
VS | 05 | D8 (P0.21) | |
HS | 06 | A1 (P0.05) | |
PCLK | 07 | A0 (P0.04) | |
XCLK | 08 | D9 (P0.27) | |
D7 | 09 | D4 (P1.15) | |
D6 | 10 | D8 (P1.14) | |
D5 | 11 | D5 (P1.13) | |
D4 | 12 | D3 (P1.12) | |
D3 | 13 | D2 (P1.11) | |
D2 | 14 | D0/RX (P1.10) | |
D1 | 15 | D1/TX (P1.03) | |
D0 | 16 | D10 (P1.02) | |
RESET | 17 | A2 (P0.30) | 可不接 |
PWDN | 18 | A3 (P0.29) | 可不接 |
Fig. 31-1 Arduino Nano 33 BLE Sense連接OV7670攝影機模組(無FIFO)之參考線路圖。(OmniXRI整理繪製, 2021/10/21)
由於BLE Sense上並沒有顯示元件(如LCD),所以由OV7670取得的影像必須以二進制(16bit, RGB565)格式數值,經由虛擬串列埠(Virtual COM)傳送到電腦上顯示。為了測試OV7670是否能正確取像,首先要在Arduino IDE安裝必要程式庫。點擊主選單[草稿碼]-[匯入程式庫]-[管理程式庫...],再輸入「OV7670」,選擇「Arduino_OV767x」,按下安裝即可。
接著點擊主選單[檔案]-[範例],結果發現「Arduino_OV767x」竟然被歸類在不相容的函式庫中,正在一頭霧水時,回頭檢查「開發板」設定時,發現原先選用的是「Arduino Mbed OS Nano Boards」下的「Arduino Nano 33 BLE」,而另一個「Arduino Mbed OS Boards」下也有一個同名的「Arduino Nano 33 BLE」,經更換成後者後,「Arduino_OV767x」就正確回到「第三方程式庫的範例」中了。點擊「CameraCaptureRawBytes」就可開啟測試OV7670的範例了。完整程序如圖Fig. 31-2所示。
Fig. 31-2 Arduino IDE OV7670程式庫安裝及開啟範例。(OmniXRI整理繪製, 2021/10/21)
/*
OV767X - Camera Capture Raw Bytes
點擊Arduino IDE主選單[範例]-[Arduino_OV767x]-[CameraCaptureRawBytes]得到此範例
電路連接:
- Arduino Nano 33 BLE board
- OV7670 camera module:
- 3.3 connected to 3.3
- GND connected GND
- SIOC connected to A5
- SIOD connected to A4
- VSYNC connected to 8
- HREF connected to A1
- PCLK connected to A0
- XCLK connected to 9
- D7 connected to 4
- D6 connected to 6
- D5 connected to 5
- D4 connected to 3
- D3 connected to 2
- D2 connected to 0 / RX
- D1 connected to 1 / TX
- D0 connected to 10
*/
#include <Arduino_OV767X.h> // 導入OV767x程式庫頭文件
int bytesPerFrame; // 宣告一張影像所需Byte數量
byte data[320 * 240 * 2]; // 宣告存放QVGA解析度RGB565格式(16bit)之彩色影像之緩衝區
// 設定腳位用途及模組初始化(只在電源啟動或重置時執行一次)
void setup() {
Serial.begin(9600); // 宣告虛擬串列埠傳輸速度為9600bps,可自行調整。
while (!Serial); // 若開啟不成功就一直等待。
// 初始化攝影機模組為QVGA解析度(320x240),RGB565彩色格式,取像速度為1秒1張。
// 解析度可自行調整為VGA(640x480), QVGA(320x240), QQVGA(160x120), CIF(352x240), QCIF(176x144)。
// 彩色影像格式可自行調整為YUV422, RGB444, RGB565, GRAYSCALE(8bit灰階)。
// 取像速度可設定為 1, 5, 10, 20FPS, 但由於BLE Sense CPU速度僅有64MHz,所以建議設定為1FPS為佳,以免來不及處理。
if (!Camera.begin(QVGA, RGB565, 1)) {
Serial.println("Failed to initialize camera!"); // 若初始化失敗則回傳錯誤訊息
while (1); // 令程式卡在這一行,表示程式結束。
}
bytesPerFrame = Camera.width() * Camera.height() * Camera.bytesPerPixel(); // 依實際初始化結果計算出所需回傳的Byte數量。
// Optionally, enable the test pattern for testing
// Camera.testPattern(); // 選擇性輸出,若刪除註解符號,則會一直輸出八色彩條圖,不理會實際攝影機模組拍攝到的內容。可作為測試傳輸用。
}
// 設定無窮循環程式 (會一直依序重覆執行)
void loop() {
Camera.readFrame(data); // 從攝影機讀取一個影格資料
Serial.write(data, bytesPerFrame); // 從虛擬串列埠回傳讀到的影像二進制資料
}
原則上,以上程式可直接燒錄到BLE Sense中,不用修改。這只是為了測試硬體線路用,不用太在意取像速度只有1 FPS。
剛才有提到,BLE Sense並沒有顯示功能,而透過虛擬串列埠傳送出來的值,也不是電腦顯示用的格式(如BMP),所以需要有另一個程式來解讀,這裡推薦使用Processing來接收並顯示。Processing是一種開源的程式語言,專門用來創作電子藝術和視覺互動設計。其架構是建立在Java語言之上,其整合開發環境(IDE)操作上很像Arduino IDE。
Processing不需要安裝,只需到下載頁面下載適合的作業系統的版本即可,最新的版本為4.0 beta2,亦可選用3.x版穩定版本。下載完成後只需解壓縮,不必安裝,直接執行Processing.exe(Windows版)即可。這裡並沒有要教大家如何寫程式,只需把下列範例程式複製貼上即可。其主要操作介面如圖Fig. 31-3所示。
其中有幾個小地方要手動修改一下。
/*
This sketch reads a raw Stream of RGB565 pixels
from the Serial port and displays the frame on
the window.
Use with the Examples -> CameraCaptureRawBytes Arduino sketch.
This example code is in the public domain.
範例程式來源:https://raw.githubusercontent.com/arduino-libraries/Arduino_OV767X/master/extras/CameraVisualizerRawBytes/CameraVisualizerRawBytes.pde
*/
import processing.serial.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
Serial myPort; // 宣告一串列埠
// 以下宣告值必須符合BLE Sense上傳的影像大小
final int cameraWidth = 320; // 攝影機取得影像寬度
final int cameraHeight = 240; // 攝影機取得影像高度
final int cameraBytesPerPixel = 2; // 影像像素佔用Byte數
final int bytesPerFrame = cameraWidth * cameraHeight * cameraBytesPerPixel; // 影格影像所需Byte數量
PImage myImage; // 宣告一個顯示用影像
byte[] frameBuffer = new byte[bytesPerFrame]; // 宣告影像資料緩衝區
// 設定系統相關參數,只執行一次
void setup()
{
size(320, 240); // 指定顯示視窗尺寸,需搭配取像尺寸修改
// if you have only ONE serial port active
//myPort = new Serial(this, Serial.list()[0], 9600); // if you have only ONE serial port active
// 請依不同作業系統、埠號及傳輸速度自行修改,這裡以Windows, COM3, 9600bps為例
myPort = new Serial(this, "COM3", 9600); // Windows
//myPort = new Serial(this, "/dev/ttyACM0", 9600); // Linux
//myPort = new Serial(this, "/dev/cu.usbmodem14401", 9600); // Mac
// 等待接收到足夠的Byte數量
myPort.buffer(bytesPerFrame);
myImage = createImage(cameraWidth, cameraHeight, RGB); // 創建一張RGB格式影像
}
// 繪圖函式,更新接收到的資料到視窗上
void draw()
{
image(myImage, 0, 0); // 繪製影像到視窗上
}
// 處理串列埠事件
void serialEvent(Serial myPort) {
// 從串列埠讀取原始資料到緩衝區
myPort.readBytes(frameBuffer);
// 處理原始資料透過緩衝區
ByteBuffer bb = ByteBuffer.wrap(frameBuffer);
bb.order(ByteOrder.BIG_ENDIAN);
int i = 0;
// 當資料還沒讀完
while (bb.hasRemaining()) {
// 讀取一個像素資料(16bit, RBG565)
short p = bb.getShort();
// 將RGB565格式轉換成RGB888(24bit)格式
int r = ((p >> 11) & 0x1f) << 3;
int g = ((p >> 5) & 0x3f) << 2;
int b = ((p >> 0) & 0x1f) << 3;
// 將轉換好的像素顏色繪到影像指定位置
myImage .pixels[i++] = color(r, g, b);
}
myImage .updatePixels(); // 更新影像中像素內容
}
原本以為貼上程式,按下左上角【Play】鍵就能看到完美影像,沒想到只得到一片黑呼呼的視窗,檢查了老半天,還是找不出問題,最後不得已只好換上另一個OV7670,結果就有一堆奇怪的方塊產生,感覺好像有拍到影像,但卻很破碎。於是把BLE Sense的「Camera.testPattern();」那行註解取消掉來檢查,照道理應該會得到如圖Fig. 31-3右上的彩條圖,但卻得到右下角那個歪斜的影像,感覺上好像是資料每隔一段時間就落後一些時間造成。經上網努力查找,最後得到一個解決方式,就是移到Ubuntu(Linux)下測試就不會有這個問題產生了。果然,在Ubuntu上安裝完Processing並執行同一段程式就OK了。不過第一顆OV7670還是黑畫面,猜想可能已經報銷了。於是把BLE Sense的程式改回,重新把「Camera.testPattern();」加上註解。終於可以在Processing上看到OV7670取到的影像了。但不知為何取得的影像轉了90度,只好先將就點用。
Fig. 31-3 Processing顯示BLE Sense上傳之資料。(OmniXRI整理繪製, 2021/10/21)
完成上面測試後,依Edge Impulse官網「Adding sight to your sensors」說明,應該把Edge Impulse提供的標準韌體(arduino-nano-33-ble-sense.ino.bin)使用「flash_windows.bat」(Windows版本)重新燒回BLE Sense開發板就可以了。但執行「edge-impulse-daemon --clean」將開發板連線後,但進到「資料擷取(Data Acquisition)」頁面後,在「感測器(Sensor)」欄位,卻只能看到「Build-in Accelerometer」和「Build-in Micphone」,沒看到攝影機相關選項。
在網路上查找了許久一直沒有答案,結果不小心在某個教學影片中發現,「arduino-nano-33-ble-sense.ino.bin」的日期是2021/9,而我的卻是2021/5的版本。於是死馬當活馬醫,重新下載官網提供的韌體,再次燒錄並啟動,果然,「感測器(Sensor)」欄位多了兩個選項「Camera (160x120)」、「Camera (128x96)」,且螢幕上也能呈現攝影機即時拍到的內容,雖然Processing在Windows上依舊不正常,但不影響取像結果。當按下【Start Sampling】鈕,果然可以拍下影像,並上傳到系統,如此就能建立實拍的資料集了。
Fig. 31-4 Edge Impulse連接OV7670取像結果。(OmniXRI整理繪製, 2021/10/21)
雖然經歷OV7670損壞一組、Processing在Windows上無法正常顯示BLE Sense上傳影像及Edge Impulse Firmware版本不對等問題,花了一個多星期終於搞定,還好不是在比賽期間,不然為了這個問題無法完賽,就有點可惜了。希望藉由這篇文章可以補齊原來[Day 27]、[Day 28]還沒說完的故事。
參考連結
[Day 27] Edge Impulse + BLE Sense實現影像分類(上)
[Day 28] Edge Impulse + BLE Sense實現影像分類(下)
Edge Impulse Document - Tutorials - Adding sight to your sensors
Edge Impulse Document - Development Boards - Arduino Nano 33 BLE Sense
Machine vision with low-cost camera modules